#pragma once
#include<iostream>
#include<QReadWriteLock>
#include<QMutex>
#include<unordered_map>
#include<thread>
using namespace std;

enum LockType{NOLOCK, READ, WRITE};

class ThreadLockTypeMap{
    unordered_map<thread::id, LockType> map;

    QMutex mutex;   //非读多写少情景， 反而会导致读线程饥饿。

    thread::id getThreadId(){
        return this_thread::get_id();
    }

public:
    LockType getThreadLockType(){
        mutex.lock();
        auto it = map.find(getThreadId());
        LockType lockType = it == map.end() ? NOLOCK : it->second;
        mutex.unlock();
        return lockType;
    }

    void setThreadLockType(const LockType& lockType){
        mutex.lock();
        map[getThreadId()] = lockType;
        mutex.unlock();
    }
};



class ResourceLock{        //在正常使用时可以保证安全。
    QReadWriteLock* lock;
    ThreadLockTypeMap threadLockTypeMap;         //标记线程号对应的锁类型

public:

    ResourceLock():lock(new QReadWriteLock()){}

    ~ResourceLock(){ delete lock; }

    //ThreadLockTypeMap使用线程号区分， 一个线程同时只会进入一个函数， 因此不需要同步， 只需要在ThreadLockTypeMap层面保证操作安全。
    bool tryReadLock(const int& timeOut = 0){//即在ThreadLockTypeMap的两次操作之间没有线程会改当前线程号的锁类型
        LockType lockType = threadLockTypeMap.getThreadLockType();
        if(lockType == NOLOCK){
            bool isGet = lock->tryLockForRead(timeOut);
            if(isGet)
                threadLockTypeMap.setThreadLockType(READ);
            return isGet;
        }//已获得写锁上读锁不操作 不支持锁降级
        return true;
    }

    //ThreadLockTypeMap使用线程号区分， 一个线程同时只会进入一个函数， 因此不需要同步， 只需要在ThreadLockTypeMap层面保证操作安全。
    bool tryWriteLock(const int& timeOut = 0){//即在ThreadLockTypeMap的两次操作之间没有线程会改当前线程号的锁类型
        LockType lockType = threadLockTypeMap.getThreadLockType();
        if(lockType != WRITE){//支持锁升级，先解锁再加锁。
            if(lockType == READ){
                lock->unlock();
            }
            bool isGet = lock->tryLockForWrite(timeOut);
            if(isGet)
                threadLockTypeMap.setThreadLockType(WRITE);
            return isGet;
        }
        return true;
    }

    //ThreadLockTypeMap使用线程号区分， 一个线程同时只会进入一个函数， 因此不需要同步， 只需要在ThreadLockTypeMap层面保证操作安全。
    void releaseResource(){
        LockType lockType = threadLockTypeMap.getThreadLockType();
        if(lockType != NOLOCK){
            lock->unlock();
            threadLockTypeMap.setThreadLockType(NOLOCK);
        }
    }

};



